home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr48 / eev100r1.zip / POSTFIX.INC < prev    next >
Text File  |  1993-04-01  |  17KB  |  749 lines

  1. { ------------------------------------------------------------------------
  2.   POSTFIX.INC
  3.   ------------------------------------------------------------------------
  4.  
  5.   Version 1.00, Revision 1, 02/02/92 -- added TP3 RPN support   TP3.0, 5.5, 6.0
  6.   Version 1.00, Revision 0, 12/28/91 -- original release        TP5.5, 6.0
  7.  
  8.   Written by: David J. Firth
  9.               5665-A2 Parkville St.
  10.               Columbus, OH 43229
  11.  
  12.   This file provides a complete reverse polish notation (RPN) expression
  13.   evaluator.  Each part of the RPN expression needs to be separated by a
  14.   space.  The evaluator supports the following functions:
  15.  
  16.   + - * / PI ABS ARCTAN COS EXP LN SQR SQRT
  17.  
  18.   The evaluator package includes routines to read and write values
  19.   to and from variables.  Variables should be 20 or characters or
  20.   less in length.  During expression evaluation, any unrecognized
  21.   string of characters will be assumed to be a variable.
  22.  
  23.   Two procedures are provided for expression evaluation, Calculate and
  24.   CalcAndStore.  Calculate will evaluate the expression and return the
  25.   result to the caller.  CalcAndStore will evaluate the expression and
  26.   store the result in a variable.
  27.  
  28.   POSTFIX.INC has two major data structures allocated on the heap.
  29.   The first is a stack, used for the processing of RPN expressions.
  30.   The other is a linked list used to store variables.  Before the
  31.   application program uses an evaluator function, InitializeEE must
  32.   be called to initialize the data structures.  Before the
  33.   application program is ended, the procedure DestroyList should
  34.   be called to deallocate the memory taken by these structures.
  35.  
  36.   API description:
  37.  
  38.   procedure InitializeEE;                            Init data structures
  39.  
  40.   procedure StoreVariable(VariableID:str20;          Put variable in LL
  41.                           MyValue:real);
  42.  
  43.   procedure ReadVariable(VariableID:str20;           Get variable from LL
  44.                          var MyValue:real;
  45.                          var MyError:boolean);
  46.  
  47.   procedure DestroyList;                             Close data structures
  48.  
  49.   procedure Calculate(MyFormula:AnyStr;              Evaluate RPN expression
  50.                       var MyResult:real;
  51.                       var MyError:boolean);
  52.  
  53.   procedure CalcAndStore(MyFormula:AnyStr;           Evaluate/store RPN expr
  54.                          StoreID:str20;
  55.                          var MyError:boolean);
  56.  
  57.   ------------------------------------------------------------------------
  58.  
  59.   Differences between V1.00R0 and V1.00R1:
  60.  
  61.   All files and functions in Expression Evaluator Tools V1.00R0 exist
  62.   in V1.00R1 with the following modifications:
  63.  
  64.   1. V1.00R1 is written to include Turbo Pascal V3.0 by adding POSTFIX.INC,
  65.      DFSTR.INC, and TESTP3.PAS.
  66.  
  67.   Changes to the evaluator code in POSTFIX.INC (POSTFIX.PAS is unchanged):
  68.  
  69.   2. Code to test for '+' and '-' has been added to the part of Calculate
  70.      that identifies a token as a valid number.  TP3.0's Val routine will
  71.      evaluate '+' and '-' as 0.  TP5.5 sees '+' and '-' as non-numeric.
  72.   3. A new procedure, InitializeEE, must be called prior to using the
  73.      expression evaluator.  InitializeEE performs the function that the
  74.      unit initialization code block performs in the TP5.5/TP6.0 version.
  75.   4. All references to Dec and Inc are now Succ and Pred.
  76.   5. All string types are now declared with sizes.
  77.   6. All references to the 255 byte string type are now AnyStr (declared
  78.      in DFSTR.INC).
  79.  
  80.   ------------------------------------------------------------------------ }
  81.  
  82. type
  83.  
  84.   Str20 = string[20];                 {store variable IDs this way to conserve}
  85.   Str128 = string[128];
  86.  
  87.   VariablePtr = ^VariableType;        {for dynamic allocation of records }
  88.  
  89.   VariableType = record
  90.     ID    : Str20;                    {the id of the variable, with @s   }
  91.     Value : real;                     {the current value of the variable }
  92.     Next  : VariablePtr;              {hook to next record in linked list}
  93.   end; {VariableType}
  94.  
  95.   StackItemPtr = ^StackItemType;      {for dynamic allocation of records }
  96.  
  97.   StackItemType = record
  98.     Value : real;                     {the value to be "operated" upon   }
  99.     Next  : StackItemPtr;             {hook to next record in linked list}
  100.   end; {StackItemType}
  101.  
  102. var
  103.  
  104.   HPtr,                               {head of variable list       }
  105.   TPtr,                               {tail of variable list       }
  106.   SPtr  : VariablePtr;                {used to search variable list}
  107.  
  108.   STPtr : StackItemPtr;               {the top of the stack}
  109.  
  110. { ------------------------------------------------------------------------ }
  111.  
  112. function __ParamCount(MyStr:AnyStr):byte;
  113.  
  114. {this routine is a work-alike of Turbo's own ParamCount function. this
  115.  routine requires my DFStr unit to operate.}
  116.  
  117. var
  118.  
  119.   Count,
  120.   Index  : byte;
  121.  
  122. begin
  123.  
  124.   MyStr := __RemWhiteStr(MyStr,_Leading);
  125.   MyStr := __RemWhiteStr(MyStr,_Trailing);
  126.  
  127.   Count := 0;
  128.   for Index := 1 to length(MyStr) do
  129.     if MyStr[Index]=' ' then
  130.       Count := succ(Count);
  131.  
  132.   __ParamCount := Count+1;
  133.  
  134. end; {__ParamCount}
  135.  
  136. { ------------------------------------------------------------------------ }
  137.  
  138. function __ParamStr(Index:byte;MyStr:AnyStr):AnyStr;
  139.  
  140. var
  141.  
  142.   TempStr : AnyStr;
  143.   I,
  144.   J,
  145.   P,
  146.   Count   : byte;
  147.   Spaces  : array[0..256] of byte;
  148.  
  149. begin
  150.  
  151.   TempStr := '';
  152.  
  153.   fillchar(Spaces,sizeof(Spaces),0);
  154.  
  155.   Count := __ParamCount(MyStr);
  156.  
  157.   if (Index<=Count) AND (Index>0) then begin
  158.  
  159.     MyStr := __RemWhiteStr(MyStr,_Leading);
  160.     MyStr := __RemWhiteStr(MyStr,_Trailing);
  161.  
  162.     MyStr := ' ' + MyStr + ' ';
  163.  
  164.     {load Spaces}
  165.     J := 0;
  166.     for I := 1 to length(MyStr) do begin
  167.       if MyStr[I] = ' ' then begin
  168.         Spaces[J] := I;
  169.         J := succ(J);
  170.       end;
  171.     end; {for}
  172.  
  173.     {get the parameter}
  174.     TempStr := copy(MyStr,Spaces[Index-1]+1,Spaces[Index]-Spaces[Index-1]-1);
  175.  
  176.   end;
  177.  
  178.   __ParamStr := TempStr;
  179.  
  180. end; {__ParamStr}
  181.  
  182. { ------------------------------------------------------------------------ }
  183.  
  184. procedure Pop(var MyValue:real;var MyError:boolean);
  185.  
  186. var
  187.  
  188.   TempPtr : StackItemPtr;
  189.  
  190. begin
  191.  
  192.   if STPtr=nil then begin
  193.     {tried to pop empty stack -- error!}
  194.     MyValue := 0;
  195.     MyError := true;
  196.   end
  197.   else begin
  198.     {get value}
  199.     MyValue := STPtr^.Value;
  200.     MyError := false;
  201.     {dispose of the record at the top of the stack}
  202.     TempPtr := STPtr;
  203.     STPtr := STPtr^.Next;
  204.     dispose(TempPtr);
  205.   end; {if-else}
  206.  
  207. end; {Pop}
  208.  
  209. { ------------------------------------------------------------------------ }
  210.  
  211. procedure Push(MyValue:real);
  212.  
  213. var
  214.  
  215.   TempPtr : StackItemPtr;
  216.  
  217. begin
  218.  
  219.   {create record on heap for value}
  220.   new(TempPtr);
  221.   TempPtr^.Value := MyValue;
  222.  
  223.   {attach new record as top of stack}
  224.   TempPtr^.Next := STPtr;
  225.   STPtr := TempPtr;
  226.  
  227. end; {Push}
  228.  
  229. { ------------------------------------------------------------------------ }
  230.  
  231. procedure DestroyStack(MyPtr:StackItemPtr);
  232.  
  233. begin
  234.  
  235.   if MyPtr^.Next<>nil then
  236.     DestroyStack(MyPtr^.Next);
  237.  
  238.   dispose(MyPtr);
  239.  
  240. end; {DestroyStack}
  241.  
  242. { ------------------------------------------------------------------------ }
  243.  
  244. procedure GetPointerTo(VariableID:str20;var MPtr:VariablePtr);
  245.  
  246. var
  247.  
  248.   Done : boolean;
  249.   XPtr : VariablePtr;
  250.  
  251. begin
  252.  
  253.   MPtr := nil;
  254.   XPtr := HPtr;
  255.  
  256.   Done := false;
  257.   while (not Done) do begin
  258.  
  259.     if XPtr^.ID=VariableID then
  260.       MPtr := XPtr;
  261.  
  262.     if XPtr^.Next=nil then
  263.       Done := true
  264.     else
  265.       XPtr := XPtr^.Next;
  266.  
  267.   end; {while}
  268.  
  269. end; {GetPointerTo}
  270.  
  271. { ------------------------------------------------------------------------ }
  272.  
  273. procedure ReadVariable(VariableID:str20;var MyValue:real;var MyError:boolean);
  274.  
  275. var
  276.  
  277.   MPtr : VariablePtr;
  278.  
  279. begin
  280.  
  281.   MyError := false;
  282.   MyValue := 0;
  283.  
  284.   GetPointerTo(VariableID,MPtr);
  285.  
  286.   if MPtr<>nil then begin
  287.     MyValue := MPtr^.Value
  288.   end
  289.   else begin
  290.     MyError := true;
  291.   end;
  292.  
  293. end; {ReadVariable}
  294.  
  295. { ------------------------------------------------------------------------ }
  296.  
  297. procedure StoreVariable(VariableID:str20;MyValue:real);
  298.  
  299. var
  300.  
  301.   WorkingRec : VariableType;
  302.  
  303. begin
  304.  
  305.   fillchar(WorkingRec,sizeof(WorkingRec),0);
  306.   WorkingRec.ID := VariableID;
  307.   WorkingRec.Value := MyValue;
  308.  
  309.   If HPtr = nil then begin
  310.  
  311.     {this is the first record added to the list}
  312.  
  313.     New(HPtr);                                {allocate 1st record in LL }
  314.     TPtr := HPtr;                             {init tail (= head)        }
  315.     TPtr^ := WorkingRec;                      {add new record as head    }
  316.     TPtr^.Next := nil;                        {set the next link for tail}
  317.  
  318.   end
  319.   else begin
  320.  
  321.     GetPointerTo(VariableID,SPtr);
  322.  
  323.     if SPtr <> nil then begin
  324.  
  325.       {the list exists and so does the variable -- modify value}
  326.  
  327.       SPtr^.Value := MyValue;
  328.  
  329.     end
  330.     else begin
  331.  
  332.       {the list exists, but the variable doesn't -- add it}
  333.  
  334.       New(SPtr);                          {allocate new record for LL }
  335.       SPtr^ := WorkingRec;                {put info in new LL record  }
  336.       TPtr^.Next := SPtr;                 {add new record as tail     }
  337.       SPtr^.Next := nil;                  {set the new link for tail  }
  338.       TPtr := SPtr;                       {point tail to new record   }
  339.  
  340.     end; {if-else}
  341.  
  342.   end;
  343.  
  344. end; {StoreVariable}
  345.  
  346. { ------------------------------------------------------------------------- }
  347.  
  348. Procedure DestroyFieldList(TempPtr:VariablePtr);
  349.  
  350. { This procedure recursively destroys a linked list }
  351.  
  352. Begin
  353.  
  354.   If TempPtr^.Next <> nil then
  355.     DestroyFieldList(TempPtr^.Next);
  356.  
  357.   Dispose(TempPtr);
  358.  
  359. End;
  360.  
  361. { ------------------------------------------------------------------------ }
  362.  
  363. procedure DestroyList;
  364.  
  365. begin
  366.  
  367.   if HPtr <> Nil then
  368.     DestroyFieldList(HPtr);
  369.  
  370.   HPtr := nil;
  371.   TPtr := nil;
  372.   SPtr := nil;
  373.  
  374.   if STPtr<>nil then
  375.     DestroyStack(STPtr);
  376.  
  377.   STPtr := nil;
  378.  
  379. end; {DestroyList}
  380.  
  381. { ------------------------------------------------------------------------ }
  382.  
  383. procedure DoAdd(var MyError:boolean);
  384.  
  385. var
  386.  
  387.   A,B : real;
  388.  
  389. begin
  390.  
  391.   Pop(A,MyError);
  392.   if not MyError then begin
  393.     Pop(B,MyError);
  394.     if not MyError then Push(A+B)
  395.   end;
  396.  
  397. end; {DoAdd}
  398.  
  399. { ------------------------------------------------------------------------ }
  400.  
  401. procedure DoSub(var MyError:boolean);
  402.  
  403. var
  404.  
  405.   A,B : real;
  406.  
  407. begin
  408.  
  409.   Pop(A,MyError);
  410.   if not MyError then begin
  411.     Pop(B,MyError);
  412.     if not MyError then Push(B-A)
  413.   end;
  414.  
  415. end; {DoSub}
  416.  
  417. { ------------------------------------------------------------------------ }
  418.  
  419. procedure DoMul(var MyError:boolean);
  420.  
  421. var
  422.  
  423.   A,B : real;
  424.  
  425. begin
  426.  
  427.   Pop(A,MyError);
  428.   if not MyError then begin
  429.     Pop(B,MyError);
  430.     if not MyError then Push(A*B)
  431.   end;
  432.  
  433. end; {DoMul}
  434.  
  435. { ------------------------------------------------------------------------ }
  436.  
  437. procedure DoPI(var MyError:boolean);
  438.  
  439. begin
  440.  
  441.   MyError := false;
  442.   Push(3.1415927);
  443.  
  444. end; {DoPI}
  445.  
  446. { ------------------------------------------------------------------------ }
  447.  
  448. procedure DoABS(var MyError:boolean);
  449.  
  450. var
  451.  
  452.   A : real;
  453.  
  454. begin
  455.  
  456.   Pop(A,MyError);
  457.   if not MyError then begin
  458.     Push(abs(A))
  459.   end;
  460.  
  461. end; {DoABS}
  462.  
  463. { ------------------------------------------------------------------------ }
  464.  
  465. procedure DoATAN(var MyError:boolean);
  466.  
  467. {this function works in radians}
  468.  
  469. var
  470.  
  471.   A : real;
  472.  
  473. begin
  474.  
  475.   Pop(A,MyError);
  476.   if not MyError then begin
  477.     Push(arctan(A));
  478.   end;
  479.  
  480. end; {DoATAN}
  481.  
  482. { ------------------------------------------------------------------------ }
  483.  
  484. procedure DoCOS(var MyError:boolean);
  485.  
  486. {this function works in radians}
  487.  
  488. var
  489.  
  490.   A : real;
  491.  
  492. begin
  493.  
  494.   Pop(A,MyError);
  495.   if not MyError then begin
  496.     Push(cos(A));
  497.   end;
  498.  
  499. end; {DoCOS}
  500.  
  501. { ------------------------------------------------------------------------ }
  502.  
  503. procedure DoEXP(var MyError:boolean);
  504.  
  505. var
  506.  
  507.   A : real;
  508.  
  509. begin
  510.  
  511.   Pop(A,MyError);
  512.   if not MyError then begin
  513.     Push(exp(A));
  514.   end;
  515.  
  516. end; {DoEXP}
  517.  
  518. { ------------------------------------------------------------------------ }
  519.  
  520. procedure DoLN(var MyError:boolean);
  521.  
  522. var
  523.  
  524.   A : real;
  525.  
  526. begin
  527.  
  528.   Pop(A,MyError);
  529.   if not MyError then begin
  530.     Push(ln(A));
  531.   end;
  532.  
  533. end; {DoLN}
  534.  
  535. { ------------------------------------------------------------------------ }
  536.  
  537. procedure DoSQR(var MyError:boolean);
  538.  
  539. var
  540.  
  541.   A : real;
  542.  
  543. begin
  544.  
  545.   Pop(A,MyError);
  546.   if not MyError then begin
  547.     Push(A*A);
  548.   end;
  549.  
  550. end; {DoSQR}
  551.  
  552. { ------------------------------------------------------------------------ }
  553.  
  554. procedure DoSQRT(var MyError:boolean);
  555.  
  556. var
  557.  
  558.   A : real;
  559.  
  560. begin
  561.  
  562.   Pop(A,MyError);
  563.   if not MyError then begin
  564.     Push(sqrt(A));
  565.   end;
  566.  
  567. end; {DoSQRT}
  568.  
  569. { ------------------------------------------------------------------------ }
  570.  
  571. procedure DoDiv(var MyError:boolean);
  572.  
  573. var
  574.  
  575.   A,B : real;
  576.  
  577. begin
  578.  
  579.   Pop(A,MyError);
  580.   if not MyError then begin
  581.     Pop(B,MyError);
  582.     if not MyError then Push(B/A)
  583.   end;
  584.  
  585. end; {DoDiv}
  586.  
  587. { ------------------------------------------------------------------------ }
  588.  
  589. procedure Calculate(MyFormula:AnyStr;var MyResult:real;var MyError:boolean);
  590.  
  591. const
  592.  
  593.   {MyFunctions is the lookup table for valid EE operators}
  594.  
  595.   NumFunctions = 12;
  596.   MyFunctions : array[1..NumFunctions] of AnyStr = ('+',
  597.                                                     '-',
  598.                                                     '*',
  599.                                                     '/',
  600.                                                     'PI',
  601.                                                     'ABS',
  602.                                                     'ARCTAN',
  603.                                                     'COS',
  604.                                                     'EXP',
  605.                                                     'LN',
  606.                                                     'SQR',
  607.                                                     'SQRT');
  608.  
  609. var
  610.  
  611.   Index,
  612.   TokenID,
  613.   TokenNum,
  614.   NumTokens : byte;
  615.   CmdTail   : ^Str128;
  616.   Token     : AnyStr;
  617.   ValError  : integer;
  618.   ValReal   : real;
  619.   VarStr    : Str20;
  620.  
  621. begin
  622.  
  623.   {set up error condition}
  624.   MyError := false;
  625.   MyResult := 0;
  626.  
  627.   NumTokens := __ParamCount(MyFormula);
  628.  
  629.   if NumTokens>0 then begin
  630.  
  631.     TokenNum := 1;
  632.     while (TokenNum<=NumTokens) AND (not MyError) do begin
  633.  
  634.       Token := __ParamStr(TokenNum,MyFormula);
  635.  
  636.       { In TP5.5, trying to obtain the value of '+' or '-' will generate
  637.         an error.  In TP3.0, the same function will return a valid number
  638.         with a value of zero.  This fix will check for '+' and '-' first. }
  639.  
  640.       if (Token='+') OR (Token='-') then begin
  641.         {manually force POSTFIX to skip number evaluation}
  642.         ValError := 1;
  643.       end
  644.       else begin
  645.         {process the token just like previous version of POSTFIX}
  646.         val(Token,ValReal,ValError);
  647.       end; {if-else}
  648.  
  649.       if ValError=0 then begin
  650.  
  651.        {token is a valid number - push onto stack}
  652.         Push(ValReal);
  653.  
  654.       end
  655.       else begin
  656.  
  657.         {token wasn't a number, is it an operator?}
  658.  
  659.         {convert to all caps}
  660.         for Index := 1 to length(Token) do
  661.           Token[Index] := upcase(Token[Index]);
  662.  
  663.         {search valid functions}
  664.         TokenID := 0;
  665.         for Index := 1 to NumFunctions do
  666.           if MyFunctions[Index]=Token then TokenID := Index;
  667.  
  668.         case TokenID of
  669.           0: begin
  670.                {search valid variables for Token}
  671.                VarStr := copy(Token,1,20);
  672.                ReadVariable(VarStr,ValReal,MyError);
  673.                if not MyError then
  674.                  {push variable's value onto stack}
  675.                  Push(ValReal);
  676.              end; {0}
  677.           1: DoAdd(MyError);
  678.           2: DoSub(MyError);
  679.           3: DoMul(MyError);
  680.           4: DoDiv(MyError);
  681.           5: DoPI(MyError);
  682.           6: DoABS(MyError);
  683.           7: DoATAN(MyError);
  684.           8: DoCOS(MyError);
  685.           9: DoEXP(MyError);
  686.          10: DoLN(MyError);
  687.          11: DoSQR(MyError);
  688.          12: DoSQRT(MyError);
  689.         end; {case}
  690.  
  691.       end; {if-else}
  692.  
  693.       {point to next token}
  694.       TokenNum := succ(TokenNum);
  695.  
  696.     end; {while}
  697.  
  698.   end
  699.   else begin
  700.     MyError := true;
  701.   end;
  702.  
  703.   if not MyError then
  704.     {the result of the evaluator is on the stack}
  705.     Pop(MyResult,MyError)
  706.   else
  707.     {problem -- destroy stack}
  708.     if STPtr<>nil then DestroyStack(STPtr);
  709.  
  710. end; {Calculate}
  711.  
  712. { ------------------------------------------------------------------------ }
  713.  
  714. procedure CalcAndStore(MyFormula:AnyStr;StoreID:str20;var MyError:boolean);
  715.  
  716. var
  717.  
  718.   MyResult : real;
  719.  
  720. begin
  721.  
  722.   {call calculate to evaluate the expression}
  723.   Calculate(MyFormula,MyResult,MyError);
  724.  
  725.   {store the result in a variable}
  726.   if not MyError then
  727.     StoreVariable(StoreID,MyResult);
  728.  
  729. end; {Calculate}
  730.  
  731. { ------------------------------------------------------------------------ }
  732.  
  733. procedure InitializeEE;
  734.  
  735. begin {init block}
  736.  
  737.   {set up linked list to empty state}
  738.  
  739.   HPtr := nil;
  740.   TPtr := nil;
  741.   SPtr := nil;
  742.  
  743.   {set up the stack}
  744.  
  745.   STPtr := nil;
  746.  
  747. end; {InitializeEE}
  748.  
  749.